#include "drawable.h"
#include "viewer.h"
#include "scene.h"
#include <osg/PolygonMode>
#include <osgParticle/ParticleSystemUpdater>
#include <QMap>
//#include <osg/Fog>
#include "odecontroller.h"
//#include <dtCore/databasepager.h>
//#include <dtCore/view.h>
//#include <dtCore/batchisector.h>
#include "datatypedefine.h"

namespace CoreBase
{
	///the intersect traversal mask the Scene uses
	const int SCENE_INTERSECT_MASK = 0x0fffffff;

	class COREBASE_EXPORT ParticleSystemFreezer : public osg::NodeVisitor
	{
	public:
		ParticleSystemFreezer();
		void SetFreezing(bool freeze) { _freezing = freeze; }
		bool GetFreezing() const { return _freezing; }
		virtual void apply(osg::Node& node);
	private:
		bool _freezing;
		typedef std::map<osgParticle::ParticleSystem*, bool> ParticleSystemBoolMap;
		ParticleSystemBoolMap _previousFrozenState;
	};

	class SceneImpl
	{
	public:
		SceneImpl(ODEController* physicsController):_physicsController(physicsController)
			,_sceneNode(new osg::Group), _mode(Scene::POINT), _face(Scene::FRONT)
		{
			_sceneNode->setDataVariance(osg::Object::DYNAMIC);
		}
		osg::ref_ptr<ODEController> _physicsController;
		osg::ref_ptr<osg::Group> _sceneNode;
		typedef QMap<QString,osg::ref_ptr<osg::LightSource>> LightMap;
		LightMap _lights;
		typedef QVector<osg::ref_ptr<Drawable>> DrawableList;
		DrawableList _addedDrawables;
		typedef QVector<osg::observer_ptr<osgViewer::View> > ViewSet;
		ViewSet _viewSet;
		Scene::Mode _mode;
		Scene::Face _face;
		ParticleSystemFreezer _freezer;
		//osg::ref_ptr<DatabasePager> mPager;
	};

	ParticleSystemFreezer::ParticleSystemFreezer(): osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , _freezing(true)
	{
		if (_freezing)
		{
			_previousFrozenState.clear();
		}
	}

	void ParticleSystemFreezer::apply(osg::Node& node)
	{
		if (osgParticle::ParticleSystemUpdater* psu = dynamic_cast<osgParticle::ParticleSystemUpdater*>(&node))
		{
			for (unsigned int i = 0; i < psu->getNumParticleSystems(); ++i)
			{
				if (osgParticle::ParticleSystem* ps = psu->getParticleSystem(i))
				{
					if (_freezing)
					{
						// Save the previous frozen state of the ParticleSystem, so subsequent attempts
						// to unfreeze it will bring it back to the way it was.
						_previousFrozenState.insert(ParticleSystemBoolMap::value_type(ps, ps->isFrozen()));
						// Allow me to break the ice. My name is Freeze. Learn it well.
						// For it's the chilling sound of your doom. -Mr. Freeze
						ps->setFrozen(_freezing);
					}
					else
					{
						// Restore the previous state.
						ps->setFrozen(_previousFrozenState[ps]);
					}
				}
			}
		}
		traverse(node);
	}

	Scene::Scene(const QString& name): Base(name), _impl(NULL),_updateOde(false)
	{
		_impl = new SceneImpl(new ODEController(this));
		ctor();
	}

	Scene::Scene(ODEController* physicsController, const QString& name)
	   : Base(name)
	   , _impl(NULL)
	{
	   _impl = new SceneImpl(physicsController);
	   ctor();
	}

	Scene::~Scene()
	{
		// Remove the remaining DeltaDrawables from the Scene. This is redundant to help prevent
		// crash-on-exits. The "exit" message should have the same effect. This must be called
		// after the above code that destroys the ODE world.
		removeAllDrawables();
		//removeSender(&System::GetInstance());
		delete _impl;
		_impl = NULL;
	}

	osg::Group* Scene::getSceneNode()
	{
		return _impl->_sceneNode.get();
	}

	const std::pair<Scene::Face, Scene::Mode> Scene::getRenderState() const
	{
		return std::make_pair(_impl->_face,_impl->_mode);
	}

	void Scene::ctor()
	{
		//AddSender(&System::GetInstance());
		_impl->_physicsController->setMessageSender(this);
	}

	void Scene::setSceneNode(osg::Group* newscenenode)
	{
		unsigned numChildren = _impl->_sceneNode->getNumChildren();
		for (unsigned i = 0; i < numChildren; ++i)
		{
			osg::Node* child = _impl->_sceneNode->getChild(i);
			newscenenode->addChild(child);
		}
		_impl->_sceneNode->removeChildren(0, numChildren);
		_impl->_sceneNode = newscenenode;
		updateViewSet();
		SceneImpl::DrawableList dl = _impl->_addedDrawables;
		removeAllDrawables();
		SceneImpl::DrawableList::iterator iterEnd = dl.end();
		for (SceneImpl::DrawableList::iterator iter = dl.begin(); iter != iterEnd; ++iter)
		{
			addDrawable((*iter).get());
		}
		//setFog(_fogOn);
	}

	void Scene::addDrawable(Drawable* drawable)
	{
		if (drawable == NULL)
		{
			return;
		}
		_impl->_sceneNode->addChild(drawable->getOSGNode());
		drawable->addedToScene(this);
		_impl->_addedDrawables.push_back(drawable);
		//if (_impl->mPager.valid())
		//{
		//	_impl->mPager->RegisterDrawable(*drawable);
		//}
	}

	void Scene::removeDrawable(Drawable* drawable)
	{
		if (drawable == NULL)
		{
			return;
		}
		_impl->_sceneNode->removeChild(drawable->getOSGNode());
		drawable->addedToScene(NULL);
		drawable->removedFromScene(this);
		unsigned int pos = getDrawableIndex(drawable);
		if (pos < _impl->_addedDrawables.size())
		{
			_impl->_addedDrawables.erase(_impl->_addedDrawables.begin() + pos);
		}
	}

	void Scene::removeAllDrawables()
	{
		while (!_impl->_addedDrawables.empty())
		{
			if (Drawable* d = getDrawable(0))
			{
				removeDrawable(d);
			}
		}
	}

	Drawable* Scene::getDrawable(unsigned int i) const
	{
		return _impl->_addedDrawables[i].get();
	}

	unsigned int Scene::getDrawableIndex(const Drawable* drawable) const
	{
		for (unsigned int childNum = 0; childNum < _impl->_addedDrawables.size(); ++childNum)
		{
			if (_impl->_addedDrawables[childNum] == drawable)
			{
				return childNum;
			}
		}

		return _impl->_addedDrawables.size(); // node not found.
	}

	unsigned int Scene::getNumberOfAddedDrawable() const
	{
		return _impl->_addedDrawables.size();
	}

	void Scene::getDrawableChildren(QVector<Drawable*>& children,Drawable& parent) const
	{
		for (unsigned int childIdx = 0; childIdx < parent.getNumChildren(); ++childIdx)
		{
			Drawable* child = parent.getChild(childIdx);
			if (child != NULL)
			{
				children.push_back(child);
				getDrawableChildren(children, *child);
			}
		}
	}

	QVector<Drawable*> Scene::getAllDrawablesInTheScene() const
	{
		QVector<Drawable*> drawables;
		for (unsigned int drawableIdx = 0; drawableIdx < getNumberOfAddedDrawable(); drawableIdx++)
		{
			Drawable* drawable = getDrawable(drawableIdx);
			if (drawable != NULL)
			{
				drawables.push_back(drawable);
				getDrawableChildren(drawables, *drawable);
			}
		}
		return drawables;
	}

	void Scene::setRenderState(Face face, Mode mode)
	{
		_impl->_face = face;
		_impl->_mode = mode;
		osg::PolygonMode::Face myface;
		osg::PolygonMode::Mode mymode;
		switch (_impl->_face)
		{
		case FRONT:
			myface = osg::PolygonMode::FRONT;
			break;
		case BACK:
			myface = osg::PolygonMode::BACK;
			break;
		case FRONT_AND_BACK:
			myface = osg::PolygonMode::FRONT_AND_BACK;
			break;
		default:
			myface = osg::PolygonMode::FRONT;
			break;
		}
		switch (_impl->_mode)
		{
		case POINT:
			mymode = osg::PolygonMode::POINT;
			break;
		case LINE:
			mymode = osg::PolygonMode::LINE;
			break;
		case FILL:
			mymode = osg::PolygonMode::FILL;
			break;
		default:
			mymode = osg::PolygonMode::FILL;
			break;
		}
		osg::ref_ptr<osg::PolygonMode> polymode = new osg::PolygonMode;
		polymode->setMode(myface, mymode);
		osg::StateSet* stateSet = _impl->_sceneNode->getOrCreateStateSet();
		stateSet->setAttributeAndModes(polymode.get(),osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);
	}

	void Scene::enableOde(bool enable)
	{
		_updateOde = enable;
	}

	void Scene::registerCollidable(Transformable* collidable) const
	{
		if (_impl->_physicsController.valid())
		{
			_impl->_physicsController->registerCollidable(collidable);
		}
	}

	void Scene::unRegisterCollidable(Transformable* collidable) const
	{
		if (_impl->_physicsController.valid())
		{
			_impl->_physicsController->unRegisterCollidable(collidable);
		}
	}

	float Scene::getHeightOfTerrain(float x, float y)
	{
		float heightofterrain = 0.f;
		// max and min z stretch from "way up high" to "way down low"
		getHeightOfTerrain(heightofterrain, x, y, 10000.f, -10000.f);
		return heightofterrain;
	}

	bool Scene::getHeightOfTerrain(float& heightofterrain, float x, float y, float maxZ, float minZ)
	{
		bool heightFound = false;
		//{
		//	const osg::Vec3 start(x, y, maxZ);
		//	const osg::Vec3 end(x, y, minZ);
		//	RefPtr<BatchIsector> isector = new BatchIsector(this);
		//	isector->EnableAndGetISector(0).SetSectorAsLineSegment(start, end);

		//	// set the traversal mask so we don't collide with the skybox
		//	isector->SetTraversalMask(SCENE_INTERSECT_MASK);

		//	heightFound = isector->Update();

		//	if (heightFound)
		//	{
		//		osg::Vec3 hitPoint;
		//		isector->GetSingleISector(0).GetHitPoint(hitPoint);
		//		heightofterrain = hitPoint.z();
		//	}
		//}
		return heightFound;
	}

	void Scene::setGravity(const osg::Vec3& gravity) const
	{
		if (_impl->_physicsController.valid())
		{
			_impl->_physicsController->setGravity(gravity);
		}
	}

	void Scene::getGravity(osg::Vec3& vec) const
	{
		if (_impl->_physicsController.valid())
		{
			vec = _impl->_physicsController->getGravity();
		}
	}

	osg::Vec3 Scene::getGravity() const
	{
		if (_impl->_physicsController.valid())
		{
			return _impl->_physicsController->getGravity();
		}
		else
		{
			return osg::Vec3(0.f, 0.f, 0.f);
		}
	}

	void Scene::getGravity(float& x, float& y, float& z) const
	{
		if (_impl->_physicsController.valid())
		{
			osg::Vec3 grav = _impl->_physicsController->getGravity();
			x = grav[0]; y = grav[1]; z = grav[2];
		}
	}

	dSpaceID Scene::getSpaceID() const
	{
		if (_impl->_physicsController.valid())
		{
			return _impl->_physicsController->getSpaceID();
		}
		else
		{
			return 0;
		}
	}

	dWorldID Scene::getWorldID() const
	{
		if (_impl->_physicsController.valid())
		{
			return _impl->_physicsController->getWorldID();
		}
		else
		{
			return 0;
		}
	}

	dJointGroupID Scene::getContactJointGroupID() const
	{
		if (_impl->_physicsController.valid())
		{
			return _impl->_physicsController->getContactJointGroupID();
		}
		else
		{
			return 0;
		}
	}

	void Scene::postEventTraversal(double time)
	{
		sendMessage(SYSTEM_MESSAGE_POST_ENENT_TRAVERSAL);
	}

	void Scene::frameStepUpdate(double)
	{
		sendMessage(SYSTEM_MESSAGE_FRAME);
	}

	void Scene::prePhysicsStepUpdate(double time)
	{
		if (_updateOde && _impl->_physicsController.valid())
		{
			_impl->_physicsController->iterate(time);
		}
		//double userData[2] = { deltaSimTime, deltaRealTime };
		sendMessage(SYSTEM_MESSAGE_PRE_FRAME);
	}

	void Scene::postPhysicsStepUpdate(double time)
	{
		sendMessage(SYSTEM_MESSAGE_POST_FRAME);
	}

	void Scene::onMessage(MessageData* data)
	{
		//else if (data->message == System::MESSAGE_PAUSE_START)
		//{
		//	// Freeze all particle systems.
		//	_impl->_freezer.SetFreezing(true);
		//	GetSceneNode()->accept(_impl->_freezer);
		//}
		//else if (data->message == System::MESSAGE_PAUSE_END)
		//{
		//	// Unfreeze all particle systems.
		//	_impl->_freezer.SetFreezing(false);
		//	GetSceneNode()->accept(_impl->_freezer);
		//}
	}

	void Scene::setUserCollisionCallback(dNearCallback* func, void* data) const
	{
		if (_impl->_physicsController.valid())
		{
			_impl->_physicsController->setUserCollisionCallback(func, data);
		}
	}

	dNearCallback* Scene::getUserCollisionCallback() const
	{
		return _impl->_physicsController.valid() ? _impl->_physicsController->getUserCollisionCallback() : NULL;
	}

	void* Scene::getUserCollisionData()
	{
		return _impl->_physicsController.valid() ? _impl->_physicsController->getUserCollisionData() : NULL;
	}

	const void* Scene::getUserCollisionData() const
	{
		return _impl->_physicsController.valid() ? _impl->_physicsController->getUserCollisionData() : NULL;
	}

	osg::Light* Scene::getLight(const QString& name)
	{
		SceneImpl::LightMap::iterator found = _impl->_lights.find(name);
		if (found != _impl->_lights.end())
		{
			return found.value()->getLight();
		}
		else
		{
			return NULL;
		}
	}

	const osg::Light* Scene::getLight(const QString& name) const
	{
		SceneImpl::LightMap::iterator found = _impl->_lights.find(name);
		if (found != _impl->_lights.end())
		{
			return found.value()->getLight();
		}
		else
		{
			return NULL;
		}
	}

	void Scene::registerLight(const QString& key,osg::LightSource* light)
	{
		if(!_impl->_lights.contains(key))
		{
			_impl->_lights.insert(key,light);
		}
	}

	void Scene::unRegisterLight(const QString& key)
	{
		_impl->_lights.remove(key);
	}

	void Scene::setSceneLightModel(osg::ref_ptr<osg::LightModel> model)
	{
		osg::StateSet* stateSet = _impl->_sceneNode->getOrCreateStateSet();
		stateSet->setAttributeAndModes(model, osg::StateAttribute::ON);
	}

	void Scene::useSceneLight(const QString& key,bool lightState)
	{
		unsigned int index = _impl->_sceneNode->getChildIndex(_impl->_lights[key].get());
		osg::StateSet* stateSet = _impl->_sceneNode->getOrCreateStateSet();
		if(lightState)
		{
			if(index == _impl->_sceneNode->getNumChildren())
			{
				_impl->_sceneNode->addChild(_impl->_lights[key].get());
			}
			SceneImpl::LightMap::Iterator iter = _impl->_lights.begin();
			for(;iter!=_impl->_lights.end();++iter)
			{
				unsigned int havelight = _impl->_sceneNode->getChildIndex(iter.value());
				if(havelight != _impl->_sceneNode->getNumChildren())
				{
					int lightnum = iter.value()->getLight()->getLightNum();
					switch (lightnum)
					{
					case 0:
						stateSet->setMode(GL_LIGHT0,osg::StateAttribute::ON );
						break;
					case 1:
						stateSet->setMode(GL_LIGHT1,osg::StateAttribute::ON );
						break;
					case 2:
						stateSet->setMode(GL_LIGHT2,osg::StateAttribute::ON );
						break;
					case 3:
						stateSet->setMode(GL_LIGHT3,osg::StateAttribute::ON );
						break;
					case 4:
						stateSet->setMode(GL_LIGHT4,osg::StateAttribute::ON );
						break;
					case 5:
						stateSet->setMode(GL_LIGHT5,osg::StateAttribute::ON );
						break;
					case 6:
						stateSet->setMode(GL_LIGHT6,osg::StateAttribute::ON );
						break;
					case 7:
						stateSet->setMode(GL_LIGHT7,osg::StateAttribute::ON );
						break;
					default:
						break;
					}
				}
			}
		}
		else
		{
			if(index != _impl->_sceneNode->getNumChildren())
			{
				_impl->_sceneNode->removeChild(_impl->_lights[key].get());
				int lightnum = _impl->_lights[key]->getLight()->getLightNum();
				unsigned int gllightnum = GL_LIGHT0+lightnum;
				switch (lightnum)
					{
					case 0:
						stateSet->setMode(GL_LIGHT0,osg::StateAttribute::OFF );
						break;
					case 1:
						stateSet->setMode(GL_LIGHT1,osg::StateAttribute::OFF );
						break;
					case 2:
						stateSet->setMode(GL_LIGHT2,osg::StateAttribute::OFF );
						break;
					case 3:
						stateSet->setMode(GL_LIGHT3,osg::StateAttribute::OFF );
						break;
					case 4:
						stateSet->setMode(GL_LIGHT4,osg::StateAttribute::OFF );
						break;
					case 5:
						stateSet->setMode(GL_LIGHT5,osg::StateAttribute::OFF );
						break;
					case 6:
						stateSet->setMode(GL_LIGHT6,osg::StateAttribute::OFF );
						break;
					case 7:
						stateSet->setMode(GL_LIGHT7,osg::StateAttribute::OFF );
						break;
					default:
						break;
					}
			}
		}
	}

	void Scene::removeView(Viewer& view)
	{
		_impl->_viewSet.remove(_impl->_viewSet.indexOf(&view));
	}

	void Scene::addView(Viewer& view)
	{
		_impl->_viewSet.push_back(&view);
	}

	void Scene::updateViewSet()
	{
		if (_impl->_viewSet.empty() == false)
		{
			SceneImpl::ViewSet::iterator it, end = _impl->_viewSet.end();
			for (it = _impl->_viewSet.begin(); it != end; ++it)
			{
				if (it->valid())
				{
					dynamic_cast<Viewer*>((*it).get())->updateFromScene();
				}
			}
		}
	}

	bool Scene::isAssignedToView(Viewer& view) const
	{
		SceneImpl::ViewSet::const_iterator it, end = _impl->_viewSet.end();
		for (it = _impl->_viewSet.begin(); it != end; ++it)
		{
			if (it->get() == &view)
			{
				return true;
			}
		}
		return false;
	}

	//DatabasePager* Scene::GetDatabasePager() const
	//{
	//	return _impl->mPager.get();
	//}

	//void Scene::SetDatabasePager(DatabasePager* pager)
	//{
	//	_impl->mPager = pager;
	//	if (_impl->mPager.valid())
	//	{
	//		_impl->mPager->GetOsgDatabasePager()->registerPagedLODs(GetSceneNode());
	//	}
	//}

	double Scene::getPhysicsStepSize() const
	{
		if (_impl->_physicsController.valid())
		{
			return _impl->_physicsController->getPhysicsStepSize();
		}
		else
		{
			return 0.0;
		}
	}

	void Scene::setPhysicsStepSize(double stepSize) const
	{
		if (_impl->_physicsController.valid())
		{
			_impl->_physicsController->setPhysicsStepSize(stepSize);
		}
	}

	ODEController* Scene::getPhysicsController() const
	{
		return _impl->_physicsController.get();
	}

	//void Scene::ResetDatabasePager()
	//{
	//	if (_impl->mPager.valid())
	//	{
	//		_impl->mPager->GetOsgDatabasePager()->clear();
	//		_impl->mPager->GetOsgDatabasePager()->registerPagedLODs(GetSceneNode());
	//	}
	//}
} 